// Package orm
// Title 			gorm.orm.components.go
// Description 		Nacos 配置中心工厂, 实现 factory.ApplicationOrmLoaderFactory, 实现其远程加载器接口方法
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
package orm

import (
	"io"
	"log"
	"os"
	"path/filepath"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"xincan.com.cn/tarot-go/internal/common/constants"
	"xincan.com.cn/tarot-go/internal/config/factory"
)

// gormConfigComponent
// Title gormConfigComponent
// Description  Gorm 对象关系映射配置信息
// Author  alittlexincan@163.com 2024/07/22 11:36
// update  alittlexincan@163.com 2024/07/22 11:36
type gormConfigComponent struct {
	factory.BaseConfigLoader[factory.ApplicationOrmLoaderFactory]        // 基础配置加载器, 内置全局配置和日志
	Type                                                          string `mapstructure:"type" json:"type"`                                                                         // 数据库类型, mysql, sqlite, postgresql, sqlserver
	SkipDefaultTx                                                 bool   `mapstructure:"skipDefaultTx" json:"skipDefaultTx"`                                                       // 是否跳过默认事务
	CoverLogger                                                   bool   `mapstructure:"coverLogger" json:"coverLogger"`                                                           // 是否覆盖默认logger
	PreparedStmt                                                  bool   `mapstructure:"preparedStmt" json:"preparedStmt"`                                                         // 设置SQL缓存
	CloseForeignKey                                               bool   `mapstructure:"disableForeignKeyConstraintWhenMigrating" json:"disableForeignKeyConstraintWhenMigrating"` // 禁用外键约束
	TablePrefix                                                   string `mapstructure:"tablePrefix" json:"tablePrefix"`                                                           // 表前缀
	SingularTable                                                 bool   `mapstructure:"singularTable" json:"singularTable"`                                                       // 是否使用单数表名(默认复数)，启用后，User结构体表将是user
}

// ConfigLoad
// Title 			ConfigLoad
// Description 		实现 factory.ApplicationOrmLoaderFactory 接口方法, 获取Redis配置信息
// Author  			alittlexincan@163.com 							2024/07/22 11:36
// Update  			alittlexincan@163.com 							2024/07/22 11:36
// Return    		*gorm.Config    								返回对象关系映射配置实例
// Return    		factory.ApplicationOrmLoaderFactory    			返回对象关系映射配置工厂实例
func (gcc *gormConfigComponent) ConfigLoad() (*gorm.Config, factory.ApplicationOrmLoaderFactory) {

	config := gcc.App.Config

	var logPath string
	var logLevel string
	var slowSql time.Duration
	var ignoreRecordNotFoundError bool
	sql := config.Database.SQL[config.Database.Provider.Value()].(map[string]interface{})

	// 获取日志存储路径
	if sql["logpath"] != "" {
		logPath = sql["logpath"].(string)
	} else {
		logPath = config.Log.Path + "/" + config.Database.Provider.Value() + "/" + "sql.log"
	}

	// 获取日志级别
	if sql["loglevel"] != "" {
		logLevel = sql["loglevel"].(string)
	} else {
		logLevel = "info"
	}

	// 获取慢查询时间
	if sql["slowsql"] != nil {
		slow, _ := time.ParseDuration(sql["slowsql"].(string))
		slowSql = slow
	} else {
		slowSql = time.Duration(50) * time.Millisecond
	}

	// 获取是否忽略记录未找到错误
	if sql["ignorerecordnotfounderror"] != nil {
		ignoreRecordNotFoundError = sql["ignorerecordnotfounderror"].(bool)
	} else {
		ignoreRecordNotFoundError = true
	}

	// 1: 创建日志文件
	if err := os.MkdirAll(filepath.Dir(logPath), 0755); err != nil {
		gcc.Logger.Error("对象关系映射配置: 创建SQL存储目录失败", zap.String("logPath", logPath), zap.Error(err))
	}
	file, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
	if err != nil {
		gcc.Logger.Error("对象关系映射配置: 打开SQL存储目录失败", zap.String("logPath", logPath), zap.Error(err))
		panic(err)
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			gcc.Logger.Error("对象关系映射配置: 关闭日志文件失败", zap.String("logPath", logPath), zap.Error(err))
		}
	}(file)
	// 日志级别映射 error、info、warn, debug, 将debug映射为info级别
	var level logger.LogLevel
	logLevelMap := map[string]logger.LogLevel{"error": logger.Error, "info": logger.Info, "warn": logger.Warn, "debug": logger.Info}
	// 2: 创建日志, 获取日志级别
	if _, ok := logLevelMap[logLevel]; !ok {
		level = logger.Error
	}

	// 4: 同时输出到控制台和文件
	logWriter := io.MultiWriter(os.Stdout, file)
	newLogger := logger.New(
		log.New(logWriter, "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold:             slowSql,
			LogLevel:                  level,
			IgnoreRecordNotFoundError: ignoreRecordNotFoundError,
			Colorful:                  true,
		})
	// 5: 创建gorm配置
	gormConfig := &gorm.Config{
		SkipDefaultTransaction:                   gcc.SkipDefaultTx,                                                                     //是否忽略ErrRecordNotFound(未查到记录错误)
		NamingStrategy:                           schema.NamingStrategy{TablePrefix: gcc.TablePrefix, SingularTable: gcc.SingularTable}, // 命名策略
		PrepareStmt:                              gcc.PreparedStmt,                                                                      // 执行任何SQL时都会创建一个prepared statement并将其缓存，以提高后续的效率
		DisableForeignKeyConstraintWhenMigrating: gcc.CloseForeignKey,                                                                   //在AutoMigrate 或 CreateTable 时，GORM 会自动创建外键约束，若要禁用该特性，可将其设置为 true
	}
	gormConfig.Logger = newLogger
	gcc.Logger.Info("加载关系映射成功")
	return gormConfig, gcc
}

// init
// Title 			init
// Description 		初始化时自动实例化自身实例, 并在main包中导入当前包路径
// Author      		alittlexincan@163.com       			2024/07/17 11:36
// Update    		alittlexincan@163.com       			2024/07/17 11:36
func init() {
	factory.OrmCenterFactory(constants.ProviderOrmGorm, &gormConfigComponent{})
}
